package com.base.interview.devtest.service;

import com.base.interview.devtest.entity.UserEntity;
import com.base.interview.devtest.entity.UserFollowerEntity;
import com.base.interview.devtest.exception.AuthenticationException;

import com.base.interview.devtest.exception.DevTestException;
import com.base.interview.devtest.model.dto.UserDTO;
import com.base.interview.devtest.model.dto.UserLoginDTO;
import com.base.interview.devtest.model.dto.UserLogoutDTO;
import com.base.interview.devtest.model.vo.UserLoginVO;
import com.base.interview.devtest.model.vo.UserVO;
import com.base.interview.devtest.repository.UserFollowerRepository;
import com.base.interview.devtest.repository.UserRepository;
import com.base.interview.devtest.util.DateUtil;
import com.base.interview.devtest.util.PasswordManager;
import com.base.interview.devtest.util.SessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class UserService implements IUserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserFollowerRepository userFollowerRepository;

    @Override
    public UserVO register(UserDTO userDTO) {
        List<UserEntity> userEntities = userRepository.findByNameOrEmail(userDTO.getUserName(), userDTO.getEmail());
        if (!userEntities.isEmpty()) {
            throw new DevTestException("User already exists");
        }

        UserEntity userEntity = new UserEntity();
        userEntity.setEmail(userDTO.getEmail());
        userEntity.setName(userDTO.getUserName());
        userEntity.setPassword(PasswordManager.encrypt(userDTO.getPassword()));
        userEntity.setRegisterDt(new Date());
        userRepository.save(userEntity);

        return convert(userEntity);
    }

    @Override
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
        UserEntity user = userRepository.findByNameAndPassword(userLoginDTO.getLoginName(), PasswordManager.encrypt(userLoginDTO.getPassword()));
        if (user == null) {
            user = userRepository.findByEmailAndPassword(userLoginDTO.getLoginName(), PasswordManager.encrypt(userLoginDTO.getPassword()));
        }
        if (user == null) {
            throw new AuthenticationException("Login Failed, please check your login name or password");
        }
        String token = SessionManager.login(user);

        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setEmail(user.getEmail());
        userLoginVO.setToken(token);
        userLoginVO.setUserId(user.getId());
        userLoginVO.setUserName(user.getName());
        return userLoginVO;
    }

    @Override
    public void logout(UserLogoutDTO userLogoutDTO) {
        SessionManager.logout(userLogoutDTO.getToken());
    }

    @Override
    public boolean follow(Long userId) {
        UserFollowerEntity followed = userFollowerRepository.getByUserIdAndAndFollowedUserId(SessionManager.getThreadUser(), userId);
        if (followed != null) {
            throw new DevTestException("You have followed this user!");
        }
        UserFollowerEntity userFollowerEntity = new UserFollowerEntity();
        userFollowerEntity.setFollowedUserId(userId);
        userFollowerEntity.setUserId(SessionManager.getThreadUser());
        userFollowerRepository.save(userFollowerEntity);
        return true;
    }

    @Override
    public boolean unfollow(Long userId) {
        UserFollowerEntity followed = userFollowerRepository.getByUserIdAndAndFollowedUserId(SessionManager.getThreadUser(), userId);
        if (followed == null) {
            throw new DevTestException("You have unfollowed this user!");
        }
        userFollowerRepository.delete(followed);
        return true;
    }

    @Override
    public List<UserVO> retrieveFollowers(Long userId) {
        // note: below code just for simple demo
        // in production, should use join table to query
        List<UserVO> userVOList = new ArrayList<>();
        List<UserFollowerEntity> followers = userFollowerRepository.getByFollowedUserId(userId);
        followers.forEach(follower -> {
            Optional<UserEntity> userEntity = userRepository.findById(follower.getUserId());
            if (userEntity.isPresent()) {
                userVOList.add(convert(userEntity.get()));
            }
        });
        return userVOList;
    }

    @Override
    public List<UserVO> retrieveFollowed(Long userId) {
        // note: below code just for simple demo
        // in production, should use join table to query
        List<UserVO> userVOList = new ArrayList<>();
        List<UserFollowerEntity> followers = userFollowerRepository.getByUserId(userId);
        followers.forEach(follower -> {
            Optional<UserEntity> userEntity = userRepository.findById(follower.getFollowedUserId());
            if (userEntity.isPresent()) {
                userVOList.add(convert(userEntity.get()));
            }
        });
        return userVOList;
    }

    @Override
    public String getUserName(Long userId) {
        Optional<UserEntity> user = userRepository.findById(userId);
        return user.isPresent() ? user.get().getName() : "UNKNOWN";
    }

    @Override
    public List<UserVO> search(String userName) {
        List<UserEntity> searchResult = userRepository.findByNameLike("%"+userName+"%");
        return searchResult.stream().map(userEntity -> convert(userEntity)).collect(Collectors.toList());
    }

    private UserVO convert(UserEntity userEntity) {
        UserVO userVO = new UserVO();
        userVO.setCreateDate(DateUtil.format(userEntity.getRegisterDt()));
        userVO.setEmail(userEntity.getEmail());
        userVO.setUserId(userEntity.getId());
        userVO.setUserName(userEntity.getName());
        return userVO;
    }
}
